文章目录
  1. 1. 前言
  2. 2 实现生成Getter/Setter方法
    1. 2.1 定义注解
    2. 2.2 定义LombokProcessor
    3. 2.3 测试代码
  3. 3. 告一段落

[TOC]

1. 前言

本节将实现一个的Getter/Setter方法作为示例。

之前说了,通过APT(Annotation Processor Tool)我们可以获取一些基本信息,生成java代码等等(查看JSR269)。那么如果我们想要对抽象语法树进行遍历方法,那么还需要JSR199Java Compiler API的支持。

涉及到API包括com.sun.source.util.Treescom.sun.source.tree.TreeVisitorcom.sun.source.util.TreePathScanner等等。

Visitor API中,你可以校验代码是否使用了Assertgotowhile(true)等等,然后基于报错提示。

详细可以参考:http://pfmiles.github.io/blog/dynamic-java/

疑问

那么既然可以实现AST的遍历访问,那么在遍历的时候可不可以改变AST的结构呢,这样我们就能像修改字节码一样,增加我们想要的东西,如Getter/Setter方法。

严格来说按照API规范来讲是不行了,当然不严格的可以通过一个bug可以做到,这个bug让我们违背了JSR269。lombok就是利用了这点而实现的。

2 实现生成Getter/Setter方法

2.1 定义注解

1
2
3
4
5
6
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Getter {

}
1
2
3
4
5
6
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Setter {

}

2.2 定义LombokProcessor

这里使用谷歌的auto-service进行服务的注册,其实它本身也是一个注解处理器,在类上用如下注解修饰:

1
@AutoService(Processor.class)
  • processor的主要代码如下:
1
2
3
4
5
6
7
8
9
10
if (!roundEnv.processingOver()) {
for (Element element : roundEnv.getRootElements()) {
if (element.getKind().isClass()) {
// 获取语法树
JCTree tree = (JCTree) trees.getTree(element);
// 使用TreeTranslator遍历
tree.accept(new LombokTreeTranslator(treeMaker, names));
}
}
}
  • LombokTreeTranslator(继承TreeTranslator)主要代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Override
public void visitClassDef(JCClassDecl jcClassDecl) {
super.visitClassDef(jcClassDecl);
// 插入getter方法
if (!getters.isEmpty()) {
jcClassDecl.defs = jcClassDecl.defs.appendList(this.getters);
}
// 插入setter方法
if (!setters.isEmpty()) {
jcClassDecl.defs = jcClassDecl.defs.appendList(this.setters);
}
this.result = jcClassDecl;
}

@Override
public void visitVarDef(JCVariableDecl jcVariableDecl) {
super.visitVarDef(jcVariableDecl);
JCModifiers modifiers = jcVariableDecl.getModifiers();
List<JCAnnotation> annotations = modifiers.getAnnotations();
if (annotations == null || annotations.size() <= 0) {
return;
}
// 下面只是简单处理, 不考虑是否存在等等
for (JCAnnotation annotation : annotations) {
if (Getter.class.getName().equals(annotation.type.toString())) {
// 生成getter方法
JCMethodDecl getterMethod = createGetterMethod(jcVariableDecl);
this.getters = this.getters.append(getterMethod);
}
if (Setter.class.getName().equals(annotation.type.toString())) {
// 生成getter方法
JCMethodDecl setterMethod = createSetterMethod(jcVariableDecl);
this.setters = this.setters.append(setterMethod);
}
}
}

2.3 测试代码

1
2
3
4
5
6
7
8
9
10
11
12
public class TestLombokDemo {

@Getter
@Setter
private String name;

public static void main(String[] args) {
TestLombokDemo d = new TestLombokDemo();
d.setName("aaa");
System.out.println(d.getName());
}
}

执行Build > Rebuild Project,生成的class文件如图:

这里会出现IDEA会报set/get方法找不到的错误,但是执行该main方法是可以执行的。lombok的解决方案应该是提供的IDEA插件的支持,我在网上在搜了半天没解决,修改AST不同于生成java代码的方式。

我也在Stack Overflow问了把,详情见https://stackoverflow.com/questions/48605327/intellij-cannot-recognize-classes-modified-by-annotation-processor-in-target-cla。

3. 告一段落

完整代码见 https://github.com/IceMimosa/apt

文章目录
  1. 1. 前言
  2. 2 实现生成Getter/Setter方法
    1. 2.1 定义注解
    2. 2.2 定义LombokProcessor
    3. 2.3 测试代码
  3. 3. 告一段落